const { type } = require('os');

const fs = require('fs'),
    { parse } = require("@babel/parser"),
    { NodePath } = require("@babel/traverse"),
    traverse = require("@babel/traverse").default,
    types = require("@babel/types"),
    core = require("@babel/core"),
    generator = require("@babel/generator").default,
    args = process.argv.splice(2);

//抛出异常
var astCode, outputName, requireSteps = false;
var doOutput = function (v) {
    if (!outputName)
        console.log(v);
    else
        fs.appendFileSync(outputName, v + "\r\n");
}
var stopWithLog = function (stage, err, path) {
    console.log("error found at stage:" + stage);
    console.log(err);
    console.log(generator(path.node).code);
    console.log("line:" + path.node.loc.start.line + " column:" + path.node.loc.start.column);
    let lines = astCode.split("\n");
    let ln = path.node.loc.start.line - 1, lnEnd = path.node.loc.end.line - 1;
    for (let i = Math.max(0, ln - 3); i < ln; i++) console.log(lines[i]);
    console.log("----");
    for (let i = ln; i <= lnEnd; i++) console.log(lines[i]);
    let sp = ''; for (let i = 0; i < path.node.loc.start.column; i++) sp += ' '; console.log(sp + '^');
    console.log("----");
    for (let i = lnEnd + 1; i <= Math.min(lnEnd + 3, lines.length); i++) console.log(lines[i]);
    throw err;
};

var upperFirst = function (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
}
var getIdentifierPath = function (path) {    //返回Identifier节点(x返回x; x.y返回y; x.y.z返回z)
    if (!path || !path.node) return;
    if (types.isIdentifier(path.node)) return path;
    if (types.isMemberExpression(path.node)) return getIdentifierPath(path.get("property"));
    //Object(impobj_n.xxxx)返回xxxx
    if (isObjectCall(path)) return getIdentifierPath(path.get("arguments")[0]);
};
var isFunction = function (path, funcName) {
    if (!path || !types.isCallExpression(path.node)) return false;
    let callee = getIdentifierPath(path.get("callee")); if (!callee || !callee.node) return false;
    return callee.node.name === funcName;
}
var isObjectPart = function (path) {    //是否是parent.object (如果是 xx.yy.toString()则认为是 xx.yy)
    if (path.parentKey !== "object") return false;
    let parent = path.parentPath; if (parent) parent = parent.parentPath;
    if (parent && isFunction(parent, "toString")) return false;
    return true;
}
var isObjectProperty = function (path, objName, propName) {
    if (!path || !types.isMemberExpression(path.node)) return false;
    if (path.parentKey === "callee") return false;
    if (objName) {
        if (!types.isIdentifier(path.node.object, { name: objName })) return false;
    } else {
        if (!types.isIdentifier(path.node.object)) return false;
    }
    if (propName) {
        if (!types.isIdentifier(path.node.property, { name: propName })) return false;
    } else {
        if (!types.isIdentifier(path.node.property)) return false;
    }
    return !isObjectPart(path);
}
var isObject = function (path, objName) {
    if (types.isMemberExpression(path.node)) {  //impobj_xxx.prop => prop
        if (!getDefineObj(path.get("object"))) return false;
        path = path.get("property");
    }
    if (objName) {
        if (!types.isIdentifier(path.node, { name: objName })) return false;
    } else {
        if (!types.isIdentifier(path.node)) return false;
    }
    return true;
}
var isObjectAnyProperty = function (path, objName, propName) {
    if (!path || !types.isMemberExpression(path.node)) return false;
    if (!isObject(path.get("object", objName))) return false;
    if (propName) {
        if (!types.isIdentifier(path.node.property, { name: propName })) return false;
    } else {
        if (!types.isIdentifier(path.node.property)) return false;
    }
    return true;
}
var getObjectProperty = function (path, objName) {   //如果是obj.property，返回property
    if (!path || !types.isMemberExpression(path.node)) return;
    if (path.parentKey === "callee") return;
    if (!isObject(path.get("object", objName))) return false;
    if (!isObjectPart(path) && types.isIdentifier(path.node.property))
        return path.node.property.name;
}
var getObjectAnyProperty = function (path, objName) {   //如果是obj.property，返回property
    if (!path || !types.isMemberExpression(path.node)) return;
    if (!isObject(path.get("object", objName))) return false;
    if (types.isIdentifier(path.node.property)) return path.node.property.name;
}
var getObjectNumberProperty = function (path) { //如果是obj[n], 返回n
    if (!path || !types.isMemberExpression(path.node)) return;
    if (path.parentKey === "callee") return;
    if (types.isIdentifier(path.node.object) && types.isNumericLiteral(path.node.property) && !isObjectPart(path))
        return path.node.property.value;
}
var isMemberFunction = function (path, memName, funcName) {  //memName.funcName(...)
    if (!path || !types.isCallExpression(path.node)) return false;
    return isObjectAnyProperty(path.get("callee"), memName, funcName);
}
var isCreateElement = function (path) { //v.a.createElement(...)
    return isFunction(path, "createElement");
};
var isFetch = function (path) {  //fetch(...)
    return isFunction(path, "fetch");
};
let ObjDefine;
var isWebpackFunction = function (path) { //function (_argA1, _argA2, _argA3)
    return types.isFunctionExpression(path.node) && path.node.params.length == 3;
}
var fetchObjects = function (ast, procFunc) {    //获取 1: function (_argA1, _argA2, _argA3){}...
    let objs = [];
    traverse(ast, {
        ObjectExpression(path) {
            //在顶级定义
            if (!path.getFunctionParent()) for (let prop of path.get("properties")) {
                /**
                 * 1: function (_argA1, _argA2, _argA3) {
                 *  ...
                 * }
                 */
                if (!types.isLiteral(prop.node.key)) continue;
                let func = prop.get("value"); if (!isWebpackFunction(func)) continue;
                let result = procFunc(func, func.get("params"), func.get("body"));
                if (result) {
                    let key = prop.node.key.value;    //Literal
                    objs.push({ key, result })
                }
            }
        }
    });
    return objs;
}
//根据变量获取其对应的ObjDefine记录(impobj_23 返回 23:function (_argA1, _argA2, _argA3){})
var getDefineObj = function (path, key) {   //不指定key时，根据变量名获取
    if (!types.isIdentifier(path.node)) return;
    if (path.node.name.indexOf("impobj_") != 0) return;
    let n = path.node.name.substring(7), i = n.indexOf("_");
    if (i >= 0) n = n.substring(0, i);
    if (!key || key == n)
        for (let obj of ObjDefine) if (obj.key == n) return obj.result;
}
//判断变量是否为函数的参数
var Is = function (arg, param) {
    if (!types.isIdentifier(arg.node)) return false;
    let binding = arg.scope.getBinding(arg.node.name);
    return binding && binding.path == param;
}
var isObjectCall = function (path) {    //Object(xxx)
    return types.isCallExpression(path.node) && path.node.arguments.length == 1 &&
        types.isIdentifier(path.node.callee, { name: "Object" });
}
var getReference = function (path, scope, name) {    //获取本地引用及其他模块的引用
    let refPath = [];
    do {
        let myObj;
        for (let myFunc = path.getFunctionParent(); myFunc && !myObj; myFunc = myFunc.getFunctionParent())
            for (let o of ObjDefine) if (o.result == myFunc) {
                myObj = o; break;
            }
        if (!scope) scope = path.scope;
        if (!name) {
            if (!types.isIdentifier(path.node)) break;
            name = path.node.name;
        }
        let binding = scope.getBinding(name);
        if (!binding || !binding.referencePaths) break;
        let expFuncs = [];
        for (let ref of binding.referencePaths) {
            if (myObj) do {
                //查找
                // _argA3.d(_argA2, "exp_Utils", function () {
                //     return Utils;
                // });
                if (ref.parentKey != "argument") break;
                let parent = ref.parentPath;
                if (!types.isReturnStatement(parent.node)) break;
                if (parent.parentKey != "body") break;
                parent = parent.parentPath;
                if (!types.isBlockStatement(parent.node) || parent.node.body.length != 1) break;
                if (parent.parentKey != "body") break;
                parent = parent.parentPath;
                if (!types.isFunctionExpression(parent.node) || parent.node.params.length != 0) break;
                if (parent.parentKey != "arguments" || parent.key != 2) break;
                parent = parent.parentPath;
                if (!types.isCallExpression(parent.node)) break;
                if (parent.getFunctionParent() != myObj.result) break;
                let params = myObj.result.get("params");
                let callee = parent.get("callee");
                if (!types.isMemberExpression(callee.node) || !Is(callee.get("object"), params[2])) break;
                let args = parent.get("arguments");
                if (args.length != 3 || !Is(args[0], params[1]) || !types.isLiteral(args[1].node)) break;
                expFuncs.push(args[1].node.value);
                continue;
            } while (false);
            refPath.push(ref);
        }
        //查找其他模块的 impobj_n.expFuncs
        for (let expName of expFuncs) for (let o of ObjDefine) if (o != myObj) {
            o.result.traverse({
                MemberExpression(_path) {
                    if (getDefineObj(_path.get("object"), myObj.key) != myObj.result) return;
                    if (!types.isIdentifier(_path.node.property, { name: expName })) return;
                    //检查是否为Object(xxx)()函数调用
                    if (_path.parentKey == "arguments" && _path.key == 0 && isObjectCall(_path.parentPath))
                        _path = _path.parentPath;
                    refPath.push(_path);
                }
            });
        }
    } while (false);
    return refPath;
}
var getInitValue = function (path, onlyInFunc) {  //获取值的节点(常量节点或定义时初始化节点)
    if (types.isLiteral(path.node)) return path;    //常量
    if (types.isIdentifier(path.node)) {    //变量
        let binding = path.scope.getBinding(path.node.name); if (!binding || !binding.path) return;
        let name = path.node.name; path = binding.path;
        //变量定义时赋值
        if (types.isVariableDeclarator(path.node) && types.isIdentifier(path.node.id, { name: name })) {
            path = path.get("init"); if (path && path.node) return path;
            //定义时未赋值，找第一次赋值命令
            for (let refer_path of binding.constantViolations)
                if (types.isAssignmentExpression(refer_path.node, { operator: "=" }) &&
                    types.isIdentifier(refer_path.node.left, { name: name }))
                    return refer_path.get("right");
        }
        //函数形参转为调用参数
        if (!onlyInFunc && types.isIdentifier(path.node) && path.parentKey === "params") {
            if (path.parentPath.parentKey === "callee")
                return getInitValue(path.parentPath.parentPath.get("arguments")[path.key]);
            /** path = _argB1
             * 
             * _lclA29 = function (_argB1, _argB2, _argB3, _argB4, _argB5, _argB6, _argB7, _argB8, _argB9, _argB10, _argB11) {
             * ...
             *      return fetch("/login", {
             *          ...
             *          body: JSON.stringify({
             *              ...
             *              username: _argB1,
             *              password: _argB2,
             *          }),
             *      }
             * ...
             * }
             */
            if (types.isFunctionExpression(path.parentPath.node) && types.isVariableDeclarator(path.parentPath.parentPath.node)) {
                let argLength = path.parentPath.node.params.length;
                let v = path.parentPath.parentPath.get("id");   //_lclA29
                let refPath = getReference(v);
                for (let ref of refPath) if (ref.parentKey == "callee") {
                    //_lclA19._lclA29(_lclD10, _lclD11, _lclD8, _lclD9, _lclD7, _lclD12, _lclD2, _lclD3, _lclD4, _lclD5, _lclD6)
                    let parent = ref.parentPath;
                    if (types.isCallExpression(parent.node) && parent.node.arguments.length == argLength)
                        return getInitValue(parent.get("arguments")[path.key]);
                }
            }
        }
    }
};
var getStringValue = function (path) {
    let valuePath = getInitValue(path);
    if (valuePath && types.isStringLiteral(valuePath.node)) return valuePath.node.value;
};
var getElementById = function (path) {  //如果包含getElementById(...).value，返回 id
    var checkMemberExpression = function (expPath) {
        if (!types.isMemberExpression(expPath)) return;
        let { property } = expPath.node, object = expPath.get("object");
        if (!types.isIdentifier(property, { name: "value" })) return;
        if (!isFunction(object, "getElementById")) return;
        let { arguments } = object.node; if (arguments.length !== 1) return;
        return getStringValue(object.get("arguments")[0]);
    };
    if (!path) return;
    let result = checkMemberExpression(path);
    if (!result) path.traverse({
        MemberExpression(_path) {
            if (!result) result = checkMemberExpression(_path);     //只返回第一个
        }
    });
    return result;
};
var travalObjectPropries = function (path, callbackFunc) {  //遍历ObjectExpression的properties
    if (!path || !path.node || !types.isObjectExpression(path.node)) return;
    let props = path.get("properties");
    for (let prop of props) if (types.isIdentifier(prop.node.key)) {
        let ret = callbackFunc(prop.node.key.name, prop.get("value"));
        if (ret) return ret;
    }
}
var getDOMParent = function (path) {    //获取上一级的createElement节点
    for (let parent = path.parentPath; parent; parent = parent.parentPath)
        if (isCreateElement(parent)) return parent;
};
var getDOMNode = function (path, texts) {   //v.a.createElement("div", { classname:"...", ...}, ...)
    if (!isCreateElement(path)) return;
    let args = path.get("arguments"); if (args.length < 2) return;
    let tagName = getStringValue(args[0]), idName, className, onClick; if (!tagName) return;
    travalObjectPropries(args[1], function (keyName, valuePath) {
        if (keyName === "className")
            className = getStringValue(valuePath);
        else if (keyName === "id")
            idName = getStringValue(valuePath);
        else if (keyName === "onClick" && types.isFunctionExpression(valuePath.node))
            onClick = valuePath.get("body");    //BlockStatement
    });
    for (let i = 2; i < args.length; i++) {
        let txt = getStringValue(args[i]); if (txt) texts.push(txt);
    }
    return { tagName, idName, className, onClick, props: args[1] };
};
var getDOMText = function (path, texts) {
    if (!isCreateElement(path)) return;
    let args = path.get("arguments");
    for (let i = 2; i < args.length; i++) {
        let txt = getStringValue(args[i]); if (txt) texts.push(txt);
    }
};
var getTexts = function (path, texts) {
    if (path) path.traverse({
        StringLiteral(_txtPath) {
            texts.push(_txtPath.node.value);
        }
    });
};
var containsText = function (texts, txt, mustEqual) {
    if (!texts) return false;
    for (let t of texts) {
        if (mustEqual) {
            if (t === txt) return true;
        } else {
            if (t.indexOf(txt) >= 0) return true;
        }
    }
    return false;
};

function DOMField(_tagname, _idname, _classname) {
    this.tagName = _tagname;
    this.idName = _idname;
    this.className = _classname;
    this.field = function () {
        let t = "<" + this.tagName;
        if (this.idName) t += " id={" + this.idName + "}";
        if (this.className) t += " class={" + this.className + "}";
        t += ">"; return t;
    }
    this.isSame = function (domfield) {
        if (this.tagName != domfield.tagName || this.idName != domfield.idName) return false;
        let mynames = this.className ? this.className.split(" ") : [],
            fieldnames = domfield.className ? domfield.className.split(" ") : [], added;
        for (let f of mynames) if (!fieldnames.includes(f)) return false;
        return true;
    }
    this.append = function (domfield) {
        if (this.tagName != domfield.tagName) return false;
        if (this.idName) {
            if (this.idName != domfield.idName) return false;
        } else
            this.idName = domfield.idName;
        if (this.className) {
            if (domfield.className) {
                let names = this.className.split(",");
                if (!names.includes(domfield.className)) {
                    names.push(domfield.className); names.sort();
                    this.className = names.toString();
                }
            }
        } else
            this.className = domfield.className;
        return true;
    }
};
function createDOMField(domRoot) {
    return new DOMField(domRoot.tagName, domRoot.idName, domRoot.className);
};
//根据domName在domString内查找完整的名字
function createDOMFieldFromDOMString(domString, tagname, domname, nameisid) {
    for (let double = false; ;) {
        let cotation = double ? "\\\"[^\\\"]*\\\"" : "'[^']*'";
        let reg1 = new RegExp("<\\s*" + tagname + "\\s+" + (nameisid ? "id" : "class") + "\\s*=\\s*" + cotation, "gi"),
            reg2 = new RegExp(cotation, "gi");
        for (let t of domString) {
            //查找<tag class="..." 或 <tag id="..."
            let mary = t.match(reg1);
            if (mary) for (let m of mary) {
                //提取引号中的内容
                let n = m.match(reg2);
                if (n && n.length > 0) {
                    let name = n[0].substring(1, n[0].length - 1);
                    if (name.indexOf(domname) >= 0)
                        return new DOMField(tagname, nameisid ? name : undefined, !nameisid ? name : undefined);
                }
            }
        }
        if (!double) double = true; else break;
    }
}
function createDOMFieldFromSelector(loadDOMPath, seltext) {
    let domString = []; getTexts(loadDOMPath, domString);
    //如果有空格分割表示父子关系，取最后一个
    let i = seltext.lastIndexOf(" ");
    if (i >= 0) seltext = seltext.substring(i + 1);
    if (seltext.indexOf(".") >= 0) {
        let ary = seltext.split(".");
        if (!ary[0]) ary[0] = "div";
        let dom = createDOMFieldFromDOMString(domString, ary[0], ary[1], false);
        return dom ? dom : new DOMField(ary[0], undefined, ary[1]);
    } else if (seltext.indexOf("#") >= 0) {
        let ary = seltext.split("#");
        if (!ary[0]) ary[0] = "div";
        let dom = createDOMFieldFromDOMString(domString, ary[0], ary[1], true);
        return dom ? dom : new DOMField(ary[0], ary[1]);
    } else
        return new DOMField("", undefined, seltext);
};
function ParamDOMField(_showname, _tagname, _idname, _classname) {
    this.showName = _showname;
    this.value = new DOMField(_tagname, _idname, _classname);
    this.compare = function () {
        if (!this.data)
            doOutput("***[" + this.showName + "]未找到 " + this.value.field());
        else if (this.value.isSame(this.data))
            doOutput("[" + this.showName + "]相同 " + this.value.field());
        else
            doOutput("#[" + this.showName + "]已改变 " + this.value.field() + " -> " + this.data.field());
    };
    this.setCompare = function (v) {
        if (!v) return;
        if (!this.data) this.data = v;
        else if (this.data.field() === v.field()) return;
        else {
            doOutput("***[" + this.showName + "]的值被替换 " + this.data.field() + " -> " + v.field());
            this.data = v;
        }
    }
};
function ParamField(_showname, _value) {
    this.showName = _showname;
    this.value = _value;
    this.compare = function () {
        if (!this.data)
            doOutput("***[" + this.showName + "]未找到 " + this.value);
        else if (this.value === this.data)
            doOutput("[" + this.showName + "]相同 " + this.value);
        else
            doOutput("#[" + this.showName + "]已改变 " + this.value + " -> " + this.data);
    };
    this.setCompare = function (v) {
        if (!v) return;
        if (!this.data) this.data = v;
        else if (this.data === v) return;
        else {
            doOutput("***[" + this.showName + "]的值被替换 " + this.data + " -> " + v);
            this.data = v;
        }
    }
};
function ParamFieldPart(_showname, _value) {
    this.showName = _showname;
    this.value = _value;
    this.compare = function () {
        let vSet = new Set(this.value), dSet, sameSet = new Set(); if (this.data) dSet = new Set(this.data);
        for (let v of vSet) {
            if (dSet && dSet.has(v)) {
                doOutput("[" + this.showName + v + "]相同"); sameSet.add(v);
            }
        }
        for (let v of vSet) if (!sameSet.has(v)) doOutput("***[" + this.showName + v + "]未找到");
        if (dSet) for (let v of dSet) if (!sameSet.has(v)) doOutput("***[" + this.showName + v + "]新增");
    };
    this.setCompare = function (v) {
        if (!v) return;
        if (!this.data) this.data = v;
        else if (this.data.sort().toString() === v.sort().toString()) return;
        else {
            doOutput("***[" + this.showName + "]的值被替换 " + this.data.toString() + " -> " + v.toString());
            this.data = v;
        }
    }
}
function DestURL(_subname, _haveparam, _issubscribe) {
    this.subName = _subname;
    this.haveParam = _haveparam;
    this.isSubscribe = _issubscribe;
    this.field = function () {
        return this.subName + "," + (this.haveParam ? "true" : "false") + "," + (this.isSubscribe ? "true" : "false");
    }
};
function createDestURL(path, _issubscribe) {
    let v = getStringValue(path);
    if (v) return new DestURL(v, false, _issubscribe);
    if (types.isBinaryExpression(path.node) && path.node.operator === "+") {
        v = getStringValue(path.get("left"));
        if (v) return new DestURL(v, true, _issubscribe);
    }
};
function ParamDestURL(_showname, _subname, _haveparam, _issubscribe) {
    this.showName = _showname;
    this.value = new DestURL(_subname, _haveparam, _issubscribe);
    this.compare = function () {
        if (!this.data)
            doOutput("***[" + this.showName + "]未找到 " + this.value.field());
        else if (this.value.field() === this.data.field())
            doOutput("[" + this.showName + "]相同 " + this.value.field());
        else
            doOutput("#[" + this.showName + "]已改变 " + this.value.field() + " -> " + this.data.field());
    };
    this.setCompare = function (v) {
        if (!v) return;
        if (!this.data) this.data = v;
        else if (this.data.field() === v.field()) return;
        else {
            doOutput("***[" + this.showName + "]的值被替换 " + this.data.field() + " -> " + v.field());
            this.data = v;
        }
    }
};
function ParamSys_Captch() {    //登录校验码
    this.testdialogID = new ParamDOMField("测试对话框ID", "div", undefined, "wdialog wdComTest");
    this.testbuttonID = new ParamDOMField("测试对话框按钮ID", "div", undefined, "wdconfirmbtn");
    this.protocoldialogID = new ParamDOMField("说明对话框ID", "div", undefined, "wdialog wdProtocol");
    this.protocolbuttonID = new ParamDOMField("说明对话框按钮ID", "div", undefined, "wdagreebtn");
    this.bidnumberID = new ParamDOMField("投标号输入框ID", "input", "wtbusername",);
    this.bidpasswordID = new ParamDOMField("密码输入框ID", "input", "wtbpassword");
    this.imagenumberID = new ParamDOMField("校验码输入框ID", "input", "wtbcaptchavalue");
    this.idcardID = new ParamDOMField("身份证输入框ID", "input", "wtbidCard");
    this.loginbuttonID = new ParamDOMField("登录按钮ID", "div", undefined, "wsubmit");
    this.imageID = new ParamDOMField("校验码图片ID", "div", undefined, "verify-img-panel");
    this.slipID = new ParamDOMField("校验码滑块ID", "div", undefined, "verify-move-block");
    this.rotateWrapID = new ParamDOMField("旋转外框ID", "div", undefined, "control-wrap slideDragWrap");
    this.rotateBtnID = new ParamDOMField("旋转滑块ID", "div", undefined, "control-btn slideDragBtn");
    this.blockID = new ParamDOMField("拼图块ID", "div", undefined, "jigsaw-verify-main");
    this.captchServer = new ParamField("校验码图片URL", "/api/");
    this.captchGet = new ParamField("图片获取URL", "get");
    this.captchCheck = new ParamField("位置检查URL", "check");
    this.captchLength = new ParamField("校验码图片长度", "gpcaptchal");
    this.captchType = new ParamField("校验码类型", "gpcaptchatype");
    this.responsecodePre = new ParamField("校验码检查应答", "\"responsecode\":");
    this.jigsawID = new ParamField("拼图块序号ID", "jigsaw-id");
    this.compare = function () {
        doOutput("---登录校验码");
        this.testdialogID.compare(); this.testbuttonID.compare();
        this.protocoldialogID.compare(); this.protocolbuttonID.compare();
        this.bidnumberID.compare(); this.bidpasswordID.compare(); this.imagenumberID.compare(); this.idcardID.compare();
        this.loginbuttonID.compare();
        this.imageID.compare(); this.slipID.compare();
        this.rotateWrapID.compare(); this.rotateBtnID.compare(); this.blockID.compare();
        this.captchServer.compare(); this.captchGet.compare(); this.captchCheck.compare();
        this.captchLength.compare(); this.captchType.compare(); this.responsecodePre.compare();
        this.jigsawID.compare();
    };
};
function ParamSys_login() { //登录相关
    this.loginURL = new ParamField("登录URL", "/login");
    this.bidnumberPre = new ParamField("投标号前缀", "\"username\":");
    this.bidpassPre = new ParamField("投标密码前缀", "\"password\":");
    //this.captchPre = new ParamField("校验码答案前缀", "\"captchaAnswer\":");
    this.sizePre = new ParamField("屏幕尺寸前缀", "\"size\":");
    this.tradeserverPre = new ParamField("队列服务器前缀", "\"endpoints\":");
    this.hint = new ParamField("被踢下线字段", "hint");
    this.offline = new ParamField("被踢下线内容", "offline");
    this.compare = function () {
        doOutput("---登录相关");
        this.loginURL.compare();
        this.bidnumberPre.compare(); this.bidpassPre.compare(); /*this.captchPre.compare();*/
        this.sizePre.compare(); this.tradeserverPre.compare();
        this.hint.compare(); this.offline.compare();
    };
};
function ParamDEST_CardInfo() { //标书信息
    this.dest = new ParamDestURL("目标URL", "/app/cardinfo", false, true);
    this.field = new ParamFieldPart("字段", ["bidcount", "bidnumber", "name", "validdate"]);
    this.compare = function () {
        doOutput("---标书信息");
        this.dest.compare(); this.field.compare();
    };
};
function ParamDEST_Auction() { //拍卖会信息
    this.dest = new ParamDestURL("目标URL", "/app/auction", false, true);
    this.field = new ParamFieldPart("字段",
        ["[0]\\id", /*"[0]\\version",*/ "[0]\\type", "[0]\\name", "[0]\\startTime", "[0]\\endTime",
            "[0]\\updateTime", "[0]\\quota", "[0]\\limitPrice", "[0]\\lowerLimit", "[0]\\priceUp", "[0]\\priceDown",
            "[0]\\warningPrice"]);
    this.compare = function () {
        doOutput("---拍卖会信息");
        this.dest.compare(); this.field.compare();
    };
};
function ParamDEST_BidCaptcha() { //校验码
    this.dest = new ParamDestURL("目标URL", "/user/queue/bidcaptcha", false, true);
    this.prompt = new ParamField("校验码提示语", "prompt");
    this.compare = function () {
        doOutput("---校验码");
        this.dest.compare();
        this.prompt.compare();
    };
};
function ParamDEST_BidUpdate() { //提交结果
    this.dest = new ParamDestURL("目标URL", "/user/queue/bid-updates", false, true);
    this.field = new ParamFieldPart("字段",
        [/*"auctionId",*/ "requestId", "type", "code", "message", "time", "bidCount", "bidAmount", "bidType"]);
    this.chkcodeDialogID = new ParamDOMField("校验码对话框ID", "div", undefined, "whSetPriceD");
    this.progressDialogID = new ParamDOMField("进度条ID", "div", undefined, "whPriceProcessD");
    this.sCheckCodeInput = new ParamDOMField("校验码输入行", "input", "bidprice");
    this.sCheckCodeButton = new ParamDOMField("校验码确定按钮", "div", undefined, "whpdConfirm whpdBtnItem");
    this.sCheckCodeCancel = new ParamDOMField("校验码取消按钮", "div", undefined, "whpdCancel whpdBtnItem");
    this.typeEnqueue = new ParamField("入列2-2标志", "enqueue");
    this.typeDeal = new ParamField("处理2-3标志", "deal");
    this.compare = function () {
        doOutput("---提交结果");
        this.dest.compare(); this.field.compare();
        this.chkcodeDialogID.compare(); this.progressDialogID.compare();
        this.sCheckCodeInput.compare(); this.sCheckCodeButton.compare(); this.sCheckCodeCancel.compare();
        this.typeEnqueue.compare(); this.typeDeal.compare();
    };
};
function ParamDEST_Error() { //错误信息
    this.dest = new ParamDestURL("目标URL", "/user/queue/errors", false, true);
    this.field = new ParamFieldPart("字段", ["code", "message"]);
    this.compare = function () {
        doOutput("---错误信息");
        this.dest.compare(); this.field.compare();
    };
};
function ParamDEST_Message() { //结果公布
    this.dest = new ParamDestURL("目标URL", "/user/queue/messages", false, true);
    this.field = new ParamFieldPart("字段", ["bidWinning", "bidAmount", "bidTime"]);
    this.compare = function () {
        doOutput("---结果公布");
        this.dest.compare(); this.field.compare();
    };
};
function ParamDEST_AuctionID() { //公共信息
    this.dest = new ParamDestURL("目标URL", "/topic/auctions/", true, true);
    this.field = new ParamFieldPart("字段",
        ["auctionId", "auctionVersion", "status", "systemTime", "numberOfBidUsers",
            "basePrice", "basePriceTime", "content", "additionalContent"]);
    this.FID_nowTime = new ParamField("nowTime序号", 3);
    this.FID_minPrice = new ParamField("minPrice序号", 5);
    this.FID_minTime = new ParamField("minTime序号", 6);
    this.FID_totalPerson = new ParamField("totalNum序号", 4);
    this.FID_status = new ParamField("status序号", 2);
    this.bidTag = new ParamField("拍卖阶段标识", "Bid");
    this.compare = function () {
        doOutput("---公共信息");
        this.dest.compare(); this.field.compare();
        this.FID_nowTime.compare(); this.FID_minPrice.compare(); this.FID_minTime.compare();
        this.FID_totalPerson.compare(); this.FID_status.compare();
        this.bidTag.compare();
    };
};
function ParamDEST_MyBid() { //出价情况
    this.dest = new ParamDestURL("目标URL", "/app/mybid/auctions/", true, true);
    this.field = new ParamFieldPart("字段", ["[0]\\bidCount", "[0]\\dealTime", "[0]\\type", "[0]\\bidAmount"]);
    this.compare = function () {
        doOutput("---出价情况");
        this.dest.compare(); this.field.compare();
    };
};
function ParamDEST_Captcha() { //获取校验码
    this.dest = new ParamDestURL("目标URL", "/app/bidcaptcha", false, false);
    this.field = new ParamFieldPart("字段", ["auctionId", "amount"]);
    this.sBidPriceInput0 = new ParamDOMField("第一阶段输入行0", "input", undefined, "firstpro-input priceinput whfinput01");
    this.sBidPriceInput1 = new ParamDOMField("第一阶段输入行1", "input", undefined, "firstpro-input priceinput whfinput02");
    this.sBidButton1 = new ParamDOMField("第一阶段出价按钮", "div", undefined, "whfbtn");
    this.sBidPriceInput2 = new ParamDOMField("第二阶段输入行", "input", undefined, "whsetpriceinput whpriceinput");
    this.sBidButton2 = new ParamDOMField("第二阶段出价按钮", "div", undefined, "whsetpricebtn");
    this.compare = function () {
        doOutput("---获取校验码");
        this.dest.compare(); this.field.compare();
        this.sBidPriceInput0.compare(); this.sBidPriceInput1.compare(); this.sBidButton1.compare();
        this.sBidPriceInput2.compare(); this.sBidButton2.compare();
    };
};
function ParamDEST_Bid() { //出价提交
    this.dest = new ParamDestURL("目标URL", "/app/bid", false, false);
    this.field = new ParamFieldPart("字段", ["auctionId", "amount", "captchaAnswer", "submitId"]);
    this.compare = function () {
        doOutput("---出价提交");
        this.dest.compare(); this.field.compare();
    };
};
function ParamSystem() {
    this.sysinfoURL = new ParamField("系统信息URL", "/system-info");
    this.bodyID = new ParamDOMField("bodyID", "div", undefined, "wbody");
    this.loginBodyID = new ParamDOMField("登录页ID", "div", undefined, "wloginbody wbody");
    this.bidBodyID = new ParamDOMField("拍牌页ID", "div", undefined, "whomemain");
    this.alertdialogID = new ParamDOMField("提示对话框ID", "div", undefined, "walert,walert wbidalert,wralert");
    this.alertbuttonID = new ParamDOMField("提示对话框按钮ID", "div", undefined, "walertagreebtn,wralertagreebtn");
    this.queryDialogID = new ParamDOMField("查询对话框ID", "div", undefined, "wqueryprice-dialog");
    this.sQueryButton = new ParamDOMField("查询取消按钮", "div", undefined, "wprcancelbtn wprbtn wcancelquerybtn");
    this.captch = new ParamSys_Captch();
    this.login = new ParamSys_login();
    this.DEST_CardInfo = new ParamDEST_CardInfo();
    this.DEST_Auction = new ParamDEST_Auction();
    this.DEST_BidCaptcha = new ParamDEST_BidCaptcha();
    this.DEST_BidUpdate = new ParamDEST_BidUpdate();
    this.DEST_Error = new ParamDEST_Error();
    this.DEST_Message = new ParamDEST_Message();
    this.DEST_AuctionID = new ParamDEST_AuctionID();
    this.DEST_MyBid = new ParamDEST_MyBid();
    this.DEST_Captcha = new ParamDEST_Captcha();
    this.DEST_Bid = new ParamDEST_Bid();
    this.compare = function () {
        doOutput("---系统参数");
        this.sysinfoURL.compare(); this.bodyID.compare(); this.loginBodyID.compare(); this.bidBodyID.compare();
        this.alertdialogID.compare(); this.alertbuttonID.compare();
        this.queryDialogID.compare(); this.sQueryButton.compare();
        this.captch.compare();
        this.login.compare();
        this.DEST_CardInfo.compare(); this.DEST_Auction.compare(); this.DEST_BidCaptcha.compare();
        this.DEST_BidUpdate.compare(); this.DEST_Error.compare(); this.DEST_Message.compare();
        this.DEST_AuctionID.compare(); this.DEST_MyBid.compare(); this.DEST_Captcha.compare(); this.DEST_Bid.compare();
    };
}

var param = new ParamSystem();
var procVerify = function (data) {
    doOutput("analyze verify.js...");
    astCode = data;
    var ast = parse(astCode);
    let move_block_path, img_panel_path, checksucc_path;
    let move_block, img_panel, dragBtn = [], memPropName = [];
    let jigsaw_path;
    traverse(ast, {
        CallExpression(path) {
            if (isFunction(path, "unbind")) {
                let args = path.get("arguments"); if (args.length < 1) return;
                if (!types.isStringLiteral(args[0].node)) return;
                if (args[0].node.value.indexOf("mousedown") >= 0) {
                    //this.htmlDoms.move_block.unbind("mousedown touchstart")
                    let callee = path.get("callee").get("object");
                    let caller = getIdentifierPath(callee); if (!caller) return;
                    move_block = caller.node.name;
                    move_block_path = path.getFunctionParent().parentPath.parentPath;
                } else if (args[0].node.value.indexOf("click") >= 0) {
                    //this.htmlDoms.img_panel.find("img").unbind("click")
                    let callee = path.get("callee").get("object");
                    while (types.isCallExpression(callee.node)) callee = callee.get("callee").get("object");
                    let caller = getIdentifierPath(callee); if (!caller) return;
                    img_panel = caller.node.name;
                    img_panel_path = path.getFunctionParent().parentPath.parentPath;
                }
            } else if (isFunction(path, "ajax")) {
                /**$.ajax({
                 * url: this.options.server + "check",
                 * type: "post",
                 * xhrFields: {
                 *   responseType: "blob"
                 * },
                 * ...
                 * })
                 * 如果指定responseType=blob则为captchaGet,否则为captchaCheck
                 */
                let args = path.get("arguments"); if (args.length < 1) return;
                let url, method, responseType, succ;
                travalObjectPropries(args[0], function (keyName, valuePath) {
                    if (keyName === "url")
                        url = valuePath;
                    else if (keyName === "type")
                        method = getStringValue(valuePath);
                    else if (keyName === "xhrFields") {
                        travalObjectPropries(valuePath, function (k, v) {
                            if (k === "responseType") responseType = getStringValue(v);
                        });
                    } else if (keyName === "success") {
                        let funcPath = valuePath;
                        if (types.isSequenceExpression(funcPath.node)) {
                            let exps = funcPath.get("expressions");
                            if (exps.length > 0) funcPath = exps[exps.length - 1];
                        }
                        if (types.isFunctionExpression(funcPath.node)) succ = funcPath.get("body");
                    }
                });
                if (!method || method.toLowerCase() !== "post") return;
                if (!url || !types.isBinaryExpression(url) || url.node.operator !== "+") return;
                let { left, right } = url.node;
                if (!types.isMemberExpression(left) || !types.isStringLiteral(right)) return;
                if (responseType && responseType.toLowerCase() === "blob")
                    param.captch.captchGet.setCompare(right.value);
                else {
                    param.captch.captchCheck.setCompare(right.value);
                    checksucc_path = succ;
                }
            } else if (isFunction(path, "open")) {
                /**XMLHttpRequest.open("post",this.options.server + "get",...)
                 * XMLHttpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
                 * 如果指定responseType=blob则为captchaGet,否则为captchaCheck
                 */
                let args = path.get("arguments"); if (args.length < 2) return;
                let method = getStringValue(args[0]), url = args[1];
                if (!method || method.toLowerCase() !== "post") return;
                if (!url || !types.isBinaryExpression(url) || url.node.operator !== "+") return;
                let { left, right } = url.node;
                if (!types.isMemberExpression(left) || !types.isStringLiteral(right)) return;
                //查找XMLHttpRequest.responseType = "..."
                //查找XMLHttpRequest.onreadystatechange = "function(){...}"
                let responseType, succ, caller = getIdentifierPath(path.get("callee").get("object"));
                if (caller) {
                    let binding = caller.scope.getBinding(caller.node.name);
                    if (binding && binding.referencePaths) for (let refer_path of binding.referencePaths) {
                        let parent = refer_path.parentPath; if (!types.isMemberExpression(parent.node)) continue;
                        if (!types.isIdentifier(parent.node.property)) continue;
                        if (!types.isAssignmentExpression(parent.parentPath.node, { operator: "=" })) continue;
                        if (parent.node.property.name === "responseType")
                            responseType = getStringValue(parent.parentPath.get("right"));
                        else if (parent.node.property.name === "onreadystatechange") {
                            let funcPath = parent.parentPath.get("right");
                            if (types.isSequenceExpression(funcPath.node)) {
                                let exps = funcPath.get("expressions");
                                if (exps.length > 0) funcPath = exps[exps.length - 1];
                            }
                            if (types.isFunctionExpression(funcPath.node)) succ = funcPath.get("body");
                        }
                    }
                }
                if (responseType && responseType.toLowerCase() === "blob")
                    param.captch.captchGet.setCompare(right.value);
                else {
                    param.captch.captchCheck.setCompare(right.value);
                    checksucc_path = succ;
                }
            } else if (isFunction(path, "getResponseHeader")) {
                let args = path.get("arguments"); if (args.length != 1) return;
                let headName = getStringValue(args[0]);
                /**查找获取后赋值的
                 * 变量 var _lclC1 = _argC3.getResponseHeader("gpcaptchatype") 或
                 * 字段 _lclB3.captchaType=_argC3.getResponseHeader("gpcaptchatype")
                 */
                if (path.parentKey === "init" && types.isVariableDeclarator(path.parent)) {     //赋值给变量
                    let varPath = getIdentifierPath(path.parentPath.get("id"));
                    //检查是否后续有赋值给字段
                    let binding = varPath.scope.getBinding(varPath.node.name);
                    if (binding && binding.referencePaths) for (let refer_path of binding.referencePaths) {
                        if (refer_path.parentKey === "right" && types.isAssignmentExpression(refer_path.parent, { operator: "=" })) {
                            let left = refer_path.parentPath.get("left");
                            if (isObjectProperty(left)) {
                                let found; for (let p of memPropName) if (p.pName === left.node.property.name) { found = true; break; }
                                if (!found) memPropName.push({ hName: headName, pName: left.node.property.name });
                                varPath = undefined; break;
                            }
                        }
                    }
                    if (varPath) {
                        /**var f = s.getResponseHeader("gpcaptchal")
                         * l = Number(f.substring(0, 6))
                         */
                        let binding = varPath.scope.getBinding(varPath.node.name);
                        if (binding && binding.referencePaths) for (let refer_path of binding.referencePaths) {
                            if (refer_path.parentKey === "object" && types.isMemberExpression(refer_path.parent) &&
                                types.isIdentifier(refer_path.parent.property, { name: "substring" })) {
                                param.captch.captchLength.setCompare(headName); break;
                            }
                        }
                    }
                } else if (path.parentKey === "right" && types.isAssignmentExpression(path.parent, { operator: "=" })) {    //赋值给字段
                    let left = path.parentPath.get("left");
                    if (isObjectProperty(left)) {
                        /**
                         * 将headName和propertyName保存后续继续查找
                         */
                        let found; for (let p of memPropName) if (p.pName === left.node.property.name) { found = true; break; }
                        if (!found) memPropName.push({ hName: headName, pName: left.node.property.name });
                    }
                }
            } else if (isFunction(path, "each")) {
                //this.htmlDoms.img_panel.find("div").each(function(){...})
                jigsaw_path = path.getFunctionParent().parentPath.parentPath;
            }
        },
        BinaryExpression(path) {
            //this.$slideDragWrap.width() - this.$slideDragBtn.outerWidth()
            if (path.node.operator != '-' || !isFunction(path.get("left"), "width") ||
                !isFunction(path.get("right"), "outerWidth")) return;
            let w = getIdentifierPath(path.get("left").get("callee").get("object")),
                b = getIdentifierPath(path.get("right").get("callee").get("object"));
            if (w && b) dragBtn.push({
                path: path.getFunctionParent().parentPath.parentPath,
                wrap: w.node.name,
                btn: b.node.name
            });
        }
    });
    for (let p of memPropName) {    //根据properyName查找使用情况，以判断headName
        let checkCaptchType = function (path) {
            /**captchaType是唯一一个用做条件比较的，在条件体内captchaBlobLength用做函数调用参数
             *if (this.captchaType == "17") {
             *   this.captchaObj = new _lclA3(this.$element, this.options, this);
             *   this.captchaObj.render(this.captchaBlob, this.captchaBlobLength);
             * } else if (this.captchaType == "16") {
             *      ......
             * } else if (parseInt(this.captchaType) <= 15) {
             *      ......
             * }
             */
            if (!types.isBinaryExpression(path.node) ||
                path.parentKey !== "test" || !types.isIfStatement(path.parent)) return;
            param.captch.captchType.setCompare(p.hName);
            path.parentPath.get("consequent").traverse({
                MemberExpression(_path) {
                    let hName; for (let p0 of memPropName) if (p0.pName === _path.node.property.name) { hName = p0.hName; break; }
                    if (hName && _path.parentKey === "arguments" && types.isCallExpression(_path.parent))
                        param.captch.captchLength.setCompare(hName);
                }
            });
        }
        traverse(ast, {
            MemberExpression(path) {
                if (!types.isIdentifier(path.node.property, { name: p.pName })) return;
                let parent = path.parentPath;
                if (isFunction(parent, "parseInt")) parent = parent.parentPath;
                if (types.isVariableDeclarator(parent.node) && types.isIdentifier(parent.node.id)) {
                    //有赋值 var ctype = parseInt(this.captchaType);
                    let binding = parent.scope.getBinding(parent.node.id.name);
                    if (binding && binding.referencePaths) for (let refer_path of binding.referencePaths)
                        checkCaptchType(refer_path.parentPath)
                } else
                    checkCaptchType(parent);
            }
        });
    }
    if (move_block_path) move_block_path.traverse({
        ObjectExpression(path) {
            travalObjectPropries(path, function (keyName, valuePath) {
                /**this.htmlDoms = {
                 * move_block: this.$element.find(".verify-contaniner .verify-move-block"),
                 * }
                 */
                if (keyName !== move_block || !isFunction(valuePath, "find")) return;
                let args = valuePath.node.arguments; if (args.length !== 1 || !types.isStringLiteral(args[0])) return;
                param.captch.slipID.setCompare(createDOMFieldFromSelector(path.getFunctionParent(), args[0].value));
            });
        }
    });
    if (img_panel_path) img_panel_path.traverse({
        ObjectExpression(path) {
            travalObjectPropries(path, function (keyName, valuePath) {
                /**this.htmlDoms = {
                 * img_panel: this.$element.find(".point-verify-contaniner .verify-img-panel"),
                 * }
                 */
                if (keyName !== img_panel || !isFunction(valuePath, "find")) return;
                let args = valuePath.node.arguments; if (args.length !== 1 || !types.isStringLiteral(args[0])) return;
                param.captch.imageID.setCompare(createDOMFieldFromSelector(path.getFunctionParent(), args[0].value));
            });
        }
    });
    for (let drag of dragBtn) drag.path.traverse({
        AssignmentExpression(path) {
            /**
             * this.$slideDragWrap = this.$ele.find(".slideDragWrap");
             * this.$slideDragBtn = this.$ele.find(".slideDragBtn");
             */
            let keyName = getIdentifierPath(path.get("left")), valuePath = path.get("right");
            if (!keyName || !keyName.node || !isFunction(valuePath, "find")) return;
            if (keyName.node.name === drag.wrap) {
                let args = valuePath.node.arguments; if (args.length !== 1 || !types.isStringLiteral(args[0])) return;
                param.captch.rotateWrapID.setCompare(createDOMFieldFromSelector(path.getFunctionParent(), args[0].value));
            } else if (keyName.node.name === drag.btn) {
                let args = valuePath.node.arguments; if (args.length !== 1 || !types.isStringLiteral(args[0])) return;
                param.captch.rotateBtnID.setCompare(createDOMFieldFromSelector(path.getFunctionParent(), args[0].value));
            }
        }
    });
    if (checksucc_path) checksucc_path.traverse({
        BinaryExpression(path) {
            /**
             * _argC1.response.responsecode == 0
             */
            if (path.node.operator != "==" && path.node.operator != "===" &&
                path.node.operator != "!=" && path.node.operator != "!==") return;
            if (!types.isNumericLiteral(path.node.right, { value: 0 })) return;
            let left = path.get("left"); if (!types.isMemberExpression(left.node)) return;
            let prop = getIdentifierPath(left);
            if (prop && prop.node) param.captch.responsecodePre.setCompare("\"" + prop.node.name + "\":");
        }
    });
    let jigsawDOMString = []; getTexts(jigsaw_path, jigsawDOMString);
    if (jigsaw_path) jigsaw_path.traverse({
        CallExpression(path) {
            //jigsaw定义内的xxxx.attr(...)调用
            if (!isFunction(path, "attr")) return;
            let { arguments } = path.node;
            if (arguments === null) return;
            if (arguments.length === 1) {
                if (!types.isStringLiteral(arguments[0])) return;
                //只有一个字符串参数且不是常规属性
                //that.theDivId = Number(that.theDiv.attr("jigsaw-id"));
                if (!["top", "left", "src"].includes(arguments[0].value)) param.captch.jigsawID.setCompare(arguments[0].value);
            } else if (arguments.length === 2) {
                //that.htmlDoms.img_panel.find(...).attr("class", "jigsaw-verify-main");
                if (!types.isStringLiteral(arguments[0]) || !types.isStringLiteral(arguments[1])) return;
                if (arguments[0].value !== "class") return;
                if (!createDOMFieldFromDOMString(jigsawDOMString, "div", arguments[1].value, false)) return;  //在jigsaw定义内没有控件定义
                param.captch.blockID.setCompare(createDOMFieldFromSelector(jigsaw_path, "." + arguments[1].value));
            }
        }
    });
};
var procAnalyze = function (data) {
    astCode = data;
    var ast = parse(astCode);
    doOutput("analyze main_cvt.js step1...");
    ObjDefine = fetchObjects(ast, function (func, params, body) { return func; });
    let loginBody, bidBody, utilsFuncs = {}, enums = {};
    let json2csvConverter, json2csvBinding;
    function isJson2csv(path) {
        if (!json2csvConverter || !path || !path.node) return false;
        if (!types.isCallExpression(path.node)) return false;
        let callee = path.get("callee");
        if (!callee || !types.isIdentifier(callee.node, { name: json2csvConverter.node.id.name })) return false;
        for (let ref of json2csvBinding) if (callee === ref) return true;
        return false;
    };
    traverse(ast, {
        ObjectExpression(path) {     //获取utils的函数名及函数体 {key:"funcname",value:function(...){...}}
            let funcName, funcPath;
            if (travalObjectPropries(path, function (keyName, valuePath) {
                if (keyName === "key")
                    funcName = getStringValue(valuePath);
                else if (keyName === "value" && types.isFunctionExpression(valuePath.node))
                    funcPath = valuePath;
                else
                    return 0;
            }) !== undefined || !funcName || !funcPath) return;
            utilsFuncs[funcName] = funcPath;
        },
        CallExpression(path) {
            const { callee, arguments } = path.node;
            if (types.isFunctionExpression(callee) &&
                arguments.length === 1 && types.isLogicalExpression(arguments[0])) {
                /**获取枚举量
                 * function (c) {
                 *   c.INDIVIDUAL = "Individual";
                 *   c.CORPORATE = "Corporate";
                 * }(n || (n = {}))
                 */
                //检查调用参数是否为 n || (n = {}) n为枚举名称
                let arg = arguments[0];
                if (arg.operator != '||' || !types.isIdentifier(arg.left) || !types.isAssignmentExpression(arg.right, { operator: "=" }) ||
                    !types.isIdentifier(arg.right.left, { name: arg.left.name }) ||
                    !types.isObjectExpression(arg.right.right) || arg.right.right.properties.length !== 0) return;
                let enumClass = arg.left.name;
                var enumArray = {};
                const { params } = callee;
                let bodyPath = path.get("callee").get("body").get("body");
                for (let stmtPath of bodyPath) {
                    let stmt = stmtPath.node;
                    //检查赋值并获取枚举量(INDIVIDUAL)和枚举值("Individual")
                    if (!types.isExpressionStatement(stmt) || !types.isAssignmentExpression(stmt.expression)) return;
                    let exp = stmt.expression;
                    if (exp.operator !== '=' || !types.isMemberExpression(exp.left) ||
                        !types.isIdentifier(exp.left.object, { name: params[0].name })) return;
                    if (types.isIdentifier(exp.left.property)) {
                        //c.INDIVIDUAL = "Individual";
                        let enumName = exp.left.property.name;  //枚举量
                        const { confident, value } = stmtPath.get("expression").get("right").evaluate();
                        if (!confident) return;
                        enumArray[enumName] = value;    //枚举值
                    } else if (types.isAssignmentExpression(exp.left.property)) {
                        //c[c.Unspecified = -1] = "Unspecified";
                        exp = exp.left.property;
                        if (exp.operator !== '=' || !types.isMemberExpression(exp.left) ||
                            !types.isIdentifier(exp.left.object, { name: params[0].name }) ||
                            !types.isIdentifier(exp.left.property)) return;
                        let enumName = exp.left.property.name;  //枚举量
                        const { confident, value } = stmtPath.get("expression").get("left").get("property").get("right").evaluate();
                        if (!confident) return;
                        enumArray[enumName] = value;    //枚举值
                    } else
                        return;
                }
                enums[enumClass] = enumArray;
            } else if (types.isMemberExpression(callee) && types.isIdentifier(callee.property, { name: "parse" })) {
                //获取json2csvConverter, 包含papa.parse，以及公共信息字段
                if (types.isIdentifier(callee.object, { name: "JSON" })) return;
                json2csvConverter = path.getFunctionParent(); if (!json2csvConverter) return;
                json2csvBinding = getReference(json2csvConverter.get("id"));
            }
        }
    });
    doOutput("analyze main_cvt.js step2...");
    traverse(ast, {
        ReturnStatement(path) {
            if (!types.isObjectExpression(path.node.argument) || path.getFunctionParent() !== json2csvConverter) return;
            /**funcion json2csvConverter() {
             * ....
             * return {
             * 公共信息字段
             * };
             * ....
             * };
             */
            let fields;
            travalObjectPropries(path.get("argument"), function (keyName, valuePath) {
                if (!fields) fields = [];
                fields.push(keyName);
                let id = getObjectNumberProperty(valuePath);
                if (id === undefined) valuePath.traverse({
                    MemberExpression(_path) {
                        id = getObjectNumberProperty(_path);
                    }
                });
                if (id !== undefined) {
                    if (!param.DEST_AuctionID.field.id) param.DEST_AuctionID.field.id = {};
                    param.DEST_AuctionID.field.id[keyName] = id;
                }
            });
            param.DEST_AuctionID.field.setCompare(fields);
        }
    });
    doOutput("analyze main_cvt.js step3...");
    let loginDOM = [];
    function returnFetch(returnArg, insideOnclick) {   //return fetch("url", {...})
        let args = returnArg.get("arguments"); if (args.length < 2) return;
        let url = getStringValue(args[0]), isPost, postBody; if (!url) return;
        travalObjectPropries(args[1], function (keyName, valuePath) {
            if (keyName === "method") {    //method: POST
                let method = getStringValue(valuePath);
                if (method.toLowerCase() === "post") isPost = true;
            } else if (keyName === "body") {   //body: {...}
                if (types.isObjectExpression(valuePath.node))
                    postBody = valuePath;
                else if (types.isCallExpression(valuePath.node)) {   //JSON.stringify({...})
                    valuePath = valuePath.get("arguments");
                    if (valuePath && valuePath.length >= 1 && types.isObjectExpression(valuePath[0].node))
                        postBody = valuePath[0];
                }
            }
        });
        if (!insideOnclick && !isPost && !postBody) {     //不在onClick返回内 没有method和body的fetch，fetch("/system-info",{...})
            param.sysinfoURL.setCompare(url);
            return true;
        }
        if (isPost && postBody) {
            param.login.loginURL.setCompare(url);
            travalObjectPropries(postBody, function (keyName, valuePath) {
                let initValue = getInitValue(valuePath), domID = getElementById(initValue);
                if (domID)
                    loginDOM.push({ keyName, valuePath, initValue, domID });
                else if (initValue && types.isCallExpression(initValue)) {
                    //size: v        v = Utils.getClientDevice();
                    let callee = getIdentifierPath(initValue.get("callee")); if (!callee || !callee.node) return;
                    let func = utilsFuncs[callee.node.name]; if (!func) return;
                    let haveScreen; func.traverse({ //getClientDevice函数包含了对screen的引用
                        Identifier(_path) {
                            if (_path.node.name === "screen") haveScreen = true;
                        }
                    });
                    if (haveScreen) param.login.sizePre.setCompare("\"" + keyName + "\":");
                }
            });
            return true;
        }
    };
    let displayFields = {};  //显示的字段{myBid.bidCount:"bidCount",...}
    let fieldAuction/*拍卖会信息字段*/, fieldResult/*拍卖结果字段*/;
    function returnCreateElement(returnArg) {   //return v.a.createElement(...)
        let args = returnArg.get("arguments"); if (args.length < 3 || !args[0]) return;
        if (types.isMemberExpression(args[0].node) && types.isIdentifier(args[0].node.property, { name: "Fragment" })) {
            //return v.a.createElement(v.a.Fragment, null, ...)
            if (!args[1] || !types.isNullLiteral(args[1].node)) return;
            let allTexts = [], allInputs = [], allButtons = [], unknownDOM = [];
            for (let i = 2; i < args.length; i++) {
                //忽略loginbody的前面两个对话框
                // if (types.isLogicalExpression(args[i].node)) continue;
                let inputs = [], buttons = [], texts = [];
                let domRoot = getDOMNode(args[i], texts); //if (!domRoot) continue;
                let bidPageText;    //拍牌页的标识文字对应的节点
                let pubinfoFrag, bidrightFrag, loginChkcodeFrag;
                args[i].traverse({
                    CallExpression(_path) {
                        if (_path === args[i]) return;
                        let si = texts.length;
                        let domNode = getDOMNode(_path, texts); if (!domNode) return;
                        if (domNode.tagName === "input")
                            inputs.push(domNode);
                        else if (domNode.onClick)
                            buttons.push(domNode);
                        if (texts.length > si) {
                            let thisTexts = texts.slice(si);
                            if (containsText(thisTexts, "等待拍卖信息")) bidrightFrag = _path;
                            if (containsText(thisTexts, "点击刷新校验码")) loginChkcodeFrag = _path;
                            if (containsText(thisTexts, "有效期"))
                                bidPageText = _path;
                            else {
                                function getFieldNameAfterText(path, text) {   //path=createElement(...)
                                    let args = path.get("arguments");
                                    let starti;
                                    for (let i = 2; i < args.length; i++) {
                                        let txt = getStringValue(args[i]);
                                        if (txt && txt.indexOf(text) >= 0) { starti = i + 1; break; }
                                    }
                                    if (!starti) return;
                                    //在找到文字的arg之后查找x.property参数，返回property
                                    let props = new Set();
                                    for (let i = starti; i < args.length; i++) {
                                        let prop = getObjectProperty(args[i]);
                                        if (prop) props.add(prop);
                                        else args[i].traverse({
                                            MemberExpression(_path) {
                                                let prop = getObjectProperty(_path); if (prop) props.add(prop);
                                            }
                                        });
                                    }
                                    if (props.size > 0) return props;
                                }
                                function getPubinfoID(specText, paramField) {
                                    if (!containsText(thisTexts, specText)) return;
                                    let fields = getFieldNameAfterText(_path, specText), setted;
                                    if (fields) for (let field of fields)
                                        if (field in param.DEST_AuctionID.field.id) {
                                            paramField.setCompare(param.DEST_AuctionID.field.id[field]); setted = true;
                                        }
                                    return setted;
                                }
                                if (getPubinfoID("目前时间", param.DEST_AuctionID.FID_nowTime) ||
                                    getPubinfoID("最低可成交价:", param.DEST_AuctionID.FID_minPrice) ||
                                    getPubinfoID("最低可成交价出价时间", param.DEST_AuctionID.FID_minTime) ||
                                    getPubinfoID("已投标人数", param.DEST_AuctionID.FID_totalPerson)) {
                                    pubinfoFrag = _path; return;
                                }
                                function getDisplayFields(specText, fieldTag) {
                                    if (!containsText(thisTexts, specText)) return;
                                    let fields = getFieldNameAfterText(_path, specText);
                                    if (fields) for (let field of fields) {
                                        if (!(fieldTag in displayFields))
                                            displayFields[fieldTag] = field;
                                        else if (displayFields[fieldTag] !== field)
                                            doOutput("*** 显示字段" + fieldTag + "疑似值" + field);
                                    }
                                    return !!fields;
                                }
                                getDisplayFields("您第", "myBid.bidCount") || getDisplayFields("出价金额:", "myBid.bidAmount") ||
                                    getDisplayFields("出价时间:", "myBid.dealTime");
                            }
                        }
                    }
                });
                if (!!domRoot) {
                    if (bidPageText) {   //拍牌页
                        bidBody = domRoot;
                        for (let p = getDOMParent(bidPageText); p;) {   //拍牌页id = body.child，且包含识别文字的节点
                            let parent = getDOMParent(p); if (!parent) break;
                            if (parent === args[i]) {
                                param.bidBodyID.setCompare(createDOMField(getDOMNode(p))); break;
                            }
                            p = parent;
                        }
                    } else if (containsText(texts, "指南")) { //登录页
                        loginBody = domRoot;
                        param.loginBodyID.setCompare(createDOMField(domRoot));
                    } else if (containsText(texts, "须知")) {   //测试对话框
                        param.captch.protocoldialogID.setCompare(createDOMField(domRoot));
                        if (buttons.length === 1)
                            param.captch.protocolbuttonID.setCompare(createDOMField(buttons[0]));
                    } else if (containsText(texts, "兼容性")) {   //说明对话框
                        param.captch.testdialogID.setCompare(createDOMField(domRoot));
                        if (buttons.length === 1)
                            param.captch.testbuttonID.setCompare(createDOMField(buttons[0]));
                    } else if (containsText(texts, "查询") && containsText(texts, "取消")) { //查询对话框
                        param.queryDialogID.setCompare(createDOMField(domRoot));
                        if (buttons.length === 1)
                            param.sQueryButton.setCompare(createDOMField(buttons[0]));
                    } else if (containsText(texts, "提示") && buttons.length === 1) {     //提示对话框
                        let field = createDOMField(domRoot);
                        if (!param.alertdialogID.data) {
                            param.alertdialogID.data = field;
                        } else if (!param.alertdialogID.data.append(field)) {
                            doOutput("***diff infoDialog: " + domRoot.tagName + "," + domRoot.className +
                                ": input " + inputs.length + ",button " + buttons.length);
                        }
                        field = createDOMField(buttons[0]);
                        if (!param.alertbuttonID.data) {
                            param.alertbuttonID.data = field;
                        } else if (!param.alertbuttonID.data.append(field)) {
                            doOutput("***diff infoButton: " + domRoot.tagName + "," + domRoot.className +
                                ": input " + inputs.length + ",button " + buttons.length);
                        }
                        if (containsText(texts, "成交")) args[i].traverse({  //拍卖结果对话框，查找Dest_Message字段
                            MemberExpression(_path) {
                                if (isObjectCall(_path.parentPath)) return;
                                let field = getObjectProperty(_path); if (!field) return;
                                if (!fieldResult) fieldResult = new Set();
                                fieldResult.add(field);
                            }
                        });
                    } else if (containsText(texts, "出价处理", true))  //入列进度条
                        param.DEST_BidUpdate.progressDialogID.setCompare(createDOMField(domRoot));
                    else if (pubinfoFrag/*公共信息*/ || bidrightFrag/*拍牌窗口右侧*/ || loginChkcodeFrag/*登录校验码部分*/ ||
                        (domRoot.tagName === "div" && domRoot.className === "hideimg") ||
                        (domRoot.tagName === "div" && domRoot.className.trim() === "wlogoutD")) {
                    } else
                        unknownDOM.push({ domRoot, inputs, buttons });
                }
                allTexts.push.apply(allTexts, texts);
                allInputs.push.apply(allInputs, inputs); allButtons.push.apply(allButtons, buttons);
            }
            /**
             * 登录输入行
             * _lclA12.a.createElement(_lclA12.a.Fragment, null,
             *    _lclA12.a.createElement("div",...wtbusername...),
             *    _lclA12.a.createElement("div",...wtbpassword...),
             *    idCardRequired && _lclA12.a.createElement("div",...wtbidCard...)
             * )
             */
            let loginInput;
            if (containsText(allTexts, "投标号") && containsText(allTexts, "密码") && containsText(allTexts, "身份")) {
                for (let ipt of allInputs) {
                    travalObjectPropries(ipt.props, function (keyName, valuePath) {
                        if (keyName === "placeholder") {
                            let txts = [], txt = getStringValue(valuePath);
                            if (txt) txts.push(txt); else getTexts(getInitValue(valuePath), txts);
                            if (containsText(txts, "投标号"))
                                param.captch.bidnumberID.setCompare(createDOMField(ipt));
                            else if (containsText(txts, "密码"))
                                param.captch.bidpasswordID.setCompare(createDOMField(ipt));
                            else if (containsText(txts, "身份"))
                                param.captch.idcardID.setCompare(createDOMField(ipt));
                            return true;
                        }
                    });
                }
                loginInput = true;
            }
            if (containsText(allTexts, "指南")) { //登录页
                for (let btn of allButtons) btn.onClick.traverse({   //按钮
                    CallExpression(path) {
                        if (path.node.arguments.length > 10)    //login按钮onclick里有一个超过10个参数的函数调用
                            param.captch.loginbuttonID.setCompare(createDOMField(btn));
                    }
                });
            }
            if (!loginInput) for (let unknown of unknownDOM) {
                doOutput("***unknown: " + unknown.domRoot.tagName + "," + unknown.domRoot.className +
                    ": input " + unknown.inputs.length + ",button " + unknown.buttons.length);
            }
        } else {
            //return v.a.createElement(...}
            //搜集文字以及 obj.property
            let inputs = [], buttons = [], texts = [], props = [];
            let domRoot = getDOMNode(returnArg, texts); if (!domRoot) return;
            for (let i = 2; i < args.length; i++) {
                // let domRoot = getDOMNode(args[i], texts); if (!domRoot) continue;
                if (getDOMNode(args[i], texts)) args[i].traverse({
                    CallExpression(_path) {
                        if (_path === args[i]) return;
                        let si = texts.length;
                        let domNode = getDOMNode(_path, texts); if (!domNode) return;
                        if (domNode.tagName === "input")
                            inputs.push(domNode);
                        else if (domNode.onClick) {
                            domNode.texts = texts.slice(si, texts.length);  //按钮文字
                            let btnarg = _path.get("arguments");
                            for (let j = 2; j < btnarg.length; j++) {
                                getDOMText(btnarg[j], domNode.texts);
                                btnarg[j].traverse({
                                    CallExpression(__path) {
                                        getDOMText(__path, domNode.texts);
                                    }
                                });
                            }
                            buttons.push(domNode);
                        }
                    },
                    MemberExpression(_path) {
                        let prop = getObjectProperty(_path); if (!prop) return;
                        for (let p = _path, parent = _path.parentPath; parent; p = parent, parent = parent.parentPath) {
                            if (!isCreateElement(parent)) continue;
                            //只在createElement("div",{},...)的第2个参数起搜素obj.property
                            if (p.parentKey === "arguments" && p.key >= 2) props.push(prop);
                            return;
                        }
                    }
                });
            }
            if (containsText(texts, "您的出价")) {  //校验码对话框
                displayFields["chkcodeDlg"] = props;
                param.DEST_BidUpdate.chkcodeDialogID.setCompare(createDOMField(domRoot));
                for (let ipt of inputs)   //输入行
                    param.DEST_BidUpdate.sCheckCodeInput.setCompare(createDOMField(ipt));
                for (let btn of buttons) {
                    if (containsText(btn.texts, "确定"))
                        param.DEST_BidUpdate.sCheckCodeButton.setCompare(createDOMField(btn));
                    else if (containsText(btn.texts, "取消"))
                        param.DEST_BidUpdate.sCheckCodeCancel.setCompare(createDOMField(btn));
                }
            } else if (containsText(texts, "出价处理", true))  //进度条
                param.DEST_BidUpdate.progressDialogID.setCompare(createDOMField(domRoot));
            else if (containsText(texts, "输入价格")) {  //第一/二阶段输入行及按钮
                for (let btn of buttons) if (containsText(btn.texts, "出价")) {
                    if (buttons.length === 1) {  //第一阶段1个按钮
                        param.DEST_Captcha.sBidButton1.setCompare(createDOMField(btn));
                        if (inputs.length > 0) param.DEST_Captcha.sBidPriceInput0.setCompare(createDOMField(inputs[0]));
                        if (inputs.length > 1) param.DEST_Captcha.sBidPriceInput1.setCompare(createDOMField(inputs[1]));
                    } else {     //第二阶段出价按钮和加价按钮
                        param.DEST_Captcha.sBidButton2.setCompare(createDOMField(btn));
                        //最后一个输入行是出价输入行(之前有加价输入行)
                        if (inputs.length > 0)
                            param.DEST_Captcha.sBidPriceInput2.setCompare(createDOMField(inputs[inputs.length - 1]));
                    }
                    break;
                }
            }
        }
    };
    traverse(ast, {
        ReturnStatement(path) {
            let returnArg = path.get("argument");
            if (isCreateElement(returnArg))
                returnCreateElement(returnArg);
            else if (isFetch(returnArg))
                returnFetch(returnArg, false);
            else if (types.isObjectExpression(returnArg.node)) {
                /**selectAuctionPubInfo()中
                 * return {
                 * id: auction.id,
                 * type: auction.type,
                 * ...
                 * content: quotation.content,
                 * ...
                 * } 只包含公共信息以及拍卖会信息的字段，剔除公共信息字段，剩下的为拍卖会信息字段
                 */
                let varPubinfo, varAuction, fields = new Set();
                if (travalObjectPropries(returnArg, function (keyName, valuePath) {
                    var setVariables = function (node) {
                        let objName = node.object.name, propertyName = node.property.name;
                        if (propertyName in param.DEST_AuctionID.field.id) {    //公共信息字段
                            if (!varPubinfo) varPubinfo = objName;
                            else if (varPubinfo !== objName) return false;
                        } else {
                            if (!varAuction) varAuction = objName;
                            else if (varAuction !== objName) return false;
                            fields.add(propertyName);
                        }
                        return true;
                    };
                    if (isObjectProperty(valuePath)) {
                        if (!setVariables(valuePath.node)) return false;
                    } else {
                        let ret; valuePath.traverse({
                            MemberExpression(_path) {
                                if (isObjectProperty(_path) && !setVariables(_path.node)) ret = false;
                            }
                        }); if (ret !== undefined) return ret;
                    }
                }) === undefined && fields.size > 0)
                    fieldAuction = fields;
            }
        }, BinaryExpression(path) {
            //Utils.LoginCaptchaServer = n.captchaServiceUrl + "api/";
            if (path.node.operator != '+') return;
            let left = getIdentifierPath(path.get("left")); if (!left || left.node.name != "captchaServiceUrl") return;
            let right = getStringValue(path.get("right")); if (!right) return;
            param.captch.captchServer.setCompare("/" + right);
        }, AssignmentExpression(path) {
            /**r.onStompError = function (f) {
             *    if (... "offline" === f.headers.hint) {}
             *    ...
             * };
             */
            let { left, right, operator } = path.node;
            if (operator !== "=" || !types.isMemberExpression(left) || !types.isFunctionExpression(right)) return;
            if (!types.isIdentifier(left.property, { name: "onStompError" })) return;
            path.get("right").traverse({
                BinaryExpression(_path) {
                    let { left, right, operator } = _path.node;
                    if (operator !== "==" && operator !== "===" && operator !== "!=" && operator !== "!==") return;
                    let field, value;
                    if (types.isStringLiteral(left) && types.isMemberExpression(right)) {
                        field = _path.get("right"); value = _path.get("left");
                    } else if (types.isStringLiteral(right) && types.isMemberExpression(left)) {
                        field = _path.get("left"); value = _path.get("right");
                    } else
                        return;
                    let fieldName = getIdentifierPath(field.get("object")); if (!fieldName || fieldName.node.name !== "headers") return;
                    if (!types.isIdentifier(field.node.property)) return;
                    param.login.hint.setCompare(field.node.property.name);
                    param.login.offline.setCompare(getStringValue(value));
                }
            });
        }, SwitchStatement(path) {
            /**公共信息.status是唯一出现在switch条件里的公共信息字段
             * switch(f.status) {
             * case AuctionStatus.BID:
             *    唯一用到getTime函数的分支
             * case AuctionStatus.FINISHED:
             * case AuctionStatus.ISSUED:
             * ...
             * }
             */
            let switchExp = getObjectProperty(path.get("discriminant"));
            if (!switchExp || !(switchExp in param.DEST_AuctionID.field.id)) return;
            param.DEST_AuctionID.FID_status.setCompare(param.DEST_AuctionID.field.id[switchExp]);
            let cases = path.get("cases");
            for (let cs of cases) {
                let haveDateFunction;
                cs.traverse({
                    CallExpression(_path) {
                        if (isFunction(_path, "getTime")) haveDateFunction = true;
                    }
                });
                if (!haveDateFunction) continue;
                let { test } = cs.node;
                if (!types.isMemberExpression(test) || !types.isIdentifier(test.object) || !types.isIdentifier(test.property)) continue;
                if (test.object.name in enums) {
                    let enumAry = enums[test.object.name];   //AuctionStatus.BID的值
                    if (test.property.name in enumAry) param.DEST_AuctionID.bidTag.setCompare(enumAry[test.property.name]);
                }
            }
        }
    });
    for (let dom of loginDOM) {
        if (param.captch.bidnumberID.data && dom.domID === param.captch.bidnumberID.data.idName)
            param.login.bidnumberPre.setCompare("\"" + dom.keyName + "\":");
        else if (param.captch.bidpasswordID.data && dom.domID === param.captch.bidpasswordID.data.idName)
            param.login.bidpassPre.setCompare("\"" + dom.keyName + "\":");
        else if (param.captch.idcardID.data && dom.domID === param.captch.idcardID.data.idName)
            continue;
        else if (types.isIdentifier(dom.valuePath.node)) {
            //captchaid or captchAnswer
            let binding = getReference(dom.valuePath);
            for (let refer_path of binding) if (refer_path.parentKey === "arguments") {
                //检验码答案会作为参数调用 dispatch(triggerVercodePattern(captchaAnswer));
                param.captch.imagenumberID.setCompare(new DOMField("input", dom.domID));
                //param.login.captchPre.setCompare("\"" + keyName + "\":");
            }
        }
    }
    if (bidBody && loginBody && bidBody.tagName === loginBody.tagName) {    //bodyID = 登录页与拍牌页共同名称
        let bidName, loginName, isID;
        let bidId = bidBody.idName, bidClass = bidBody.className,
            loginId = loginBody.idName, loginClass = loginBody.className;
        if (bidId && loginId) {
            let bid = bidId.split(" "), login = loginId.split(" ");
            for (let i = 0; !param.bodyID.data && i < bid.length; i++) {
                for (let j = 0; j < login.length; j++) if (bid[i] === login[j]) {
                    param.bodyID.setCompare(new DOMField(bidBody.tagName, bidId, bidClass)); break;
                }
            }
        }
        if (bidClass && loginClass) {
            let bid = bidClass.split(" "), login = loginClass.split(" ");
            for (let i = 0; !param.bodyID.data && i < bid.length; i++) {
                for (let j = 0; j < login.length; j++) if (bid[i] === login[j]) {
                    param.bodyID.setCompare(new DOMField(bidBody.tagName, bidId, bidClass)); break;
                }
            }
        }
        // if (bidBody.idName && loginBody.idName) {
        //     bidName = bidBody.idName; loginName = loginBody.idName; isID = true;
        // } else if (bidBody.className && loginBody.className) {
        //     bidName = bidBody.className; loginName = loginBody.className; isID = false;
        // }
        // if (bidName && loginName) {
        //     let bid = bidName.split(" "), login = loginName.split(" ");
        //     for (let i = 0; !param.bodyID.data && i < bid.length; i++) {
        //         for (let j = 0; j < login.length; j++) if (bid[i] === login[j]) {
        //             param.bodyID.setCompare(new DOMField(bid[i], bidBody.tagName, isID)); break;
        //         }
        //     }
        // }
    }
    doOutput("analyze main_cvt.js step4...");
    function subscibe(url, subFunc) {   //subscribe(url, function(n){...})
        let params = subFunc.get("params"), body = subFunc.get("body");
        if (params.length !== 1 || !types.isIdentifier(params[0].node)) return;
        let paramName = params[0].node.name, paramBinding = getReference(params[0]);
        let urlPrased, includeSubscibe, includeHeaders, includeError;
        body.traverse({
            CallExpression(path) {
                //包含subscribe函数
                if (isFunction(path, "subscribe")) includeSubscibe = true;
            },
            MemberExpression(path) {
                //包含n.headers
                if (isObjectAnyProperty(path, paramName, "headers")) includeHeaders = true;
            },
            StringLiteral(path) {
                //包含"图像校验码错误"
                if (path.node.value.indexOf("图像校验码错误") >= 0) includeError = true;
            }
        });
        body.traverse({
            CallExpression(path) {  //查找updatexxx(a) 特征为只有一个参数的函数调用，且该参数来自arg[0]
                if (urlPrased) return;
                if (isMemberFunction(path, "JSON", "parse")) return; //忽略JSON.parse
                if (isJson2csv(path)) {  //忽略Json2csv
                    if (path.getFunctionParent() === subFunc) {  //包含Json2csv的是公共信息
                        param.DEST_AuctionID.dest.setCompare(createDestURL(url, true));
                        urlPrased = true;
                    }
                    return;
                }
                let callee = getIdentifierPath(path.get("callee")), args = path.get("arguments");
                if (!callee || args.length !== 1) return;
                let objKeys = {}/*显示字段:body字段*/, arrayIndex, jsonObj = new Set();
                function addObjKeys(keyName, valueName) {
                    if (!(keyName in objKeys))
                        objKeys[keyName] = valueName;
                    else if (objKeys[keyName] !== valueName)
                        doOutput("*** subscibe字段" + objKeys[keyName] + "疑似值" + valueName);
                }
                function getBodyField(dispFieldName) {
                    if (!(dispFieldName in displayFields)) return;
                    if (!(displayFields[dispFieldName] in objKeys)) return;
                    return objKeys[displayFields[dispFieldName]];
                }
                function getParamFields() { //从objKeys获取param.DEST_xxx.field
                    let fields = []; for (let key in objKeys) {
                        let field = objKeys[key];
                        if (arrayIndex !== undefined) field = "[" + arrayIndex + "]\\" + field;
                        fields.push(field);
                    }
                    return fields;
                }
                function isArgFromParam(arg, canPartOf) {
                    if (!arg || !arg.node) return false;
                    if (types.isMemberExpression(arg.node)) {
                        if (types.isLiteral(arg.node.property)) {   //xxx[0] -> xxx
                            arrayIndex = arg.node.property.value;
                        } else if (types.isIdentifier(arg.node.property)) { //xxx.yyy -> xxx
                            if (!canPartOf && arg.node.property.name !== "body")  //只允许xxx.body
                                return false;
                        } else if (!canPartOf)
                            return false;
                        return isArgFromParam(arg.get("object"), canPartOf);
                    }
                    if (isMemberFunction(arg, "JSON", "parse") || isJson2csv(arg)) {    //JSON.parse(xxx) -> xxx
                        if (!isArgFromParam(arg.get("arguments")[0], canPartOf)) return false;
                        let parent = arg.parentPath.node;
                        if (arg.parentKey === "init" && types.isVariableDeclarator(parent) && types.isIdentifier(parent.id))
                            jsonObj.add(parent.id.name);
                        else if (arg.parentKey === "right" && types.isAssignmentExpression(parent) &&
                            parent.operator === "=" && types.isIdentifier(parent.left))
                            jsonObj.add(parent.left.name);
                        return true;
                    }
                    if (types.isObjectExpression(arg.node)) {    //ex_updatexxx({...field:n.xx...})
                        let ret;
                        travalObjectPropries(arg, function (keyName, valuePath) {
                            if (!isArgFromParam(valuePath, true)) return;
                            addObjKeys(keyName, types.isMemberExpression(valuePath.node) ? valuePath.node.property.name : "");
                            ret = true;
                        });
                        return ret;
                    }
                    if (types.isIdentifier(arg.node)) {
                        if (arg.node.name === paramName)
                            for (let ref of paramBinding) if (ref === arg) return true;
                        return isArgFromParam(getInitValue(arg, true), canPartOf);
                    }
                    return false;
                }
                if (!isArgFromParam(args[0])) return;
                if (includeSubscibe) {  //包含subscibe的是DEST_Auction
                    objKeys = {}; if (fieldAuction) for (let fd of fieldAuction) addObjKeys(fd, fd);
                    param.DEST_Auction.field.setCompare(getParamFields());
                    param.DEST_Auction.dest.setCompare(createDestURL(url, true));
                    urlPrased = true; return;
                }
                if (includeHeaders) {    //包含n.headers的是DEST_BidCaptcha
                    /**从调用ex_updateBidCaptcha(n)的n参数定义中获取所有f.header.xxx字段
                     * n={
                     * auctionId: f.headers["auction-id"],
                     * prompt: f.headers.prompt,
                     * }
                     */
                    let catpchaHeader = {}, obj = getInitValue(args[0]);
                    if (!obj) {
                        //包含ex_updateBidCaptcha(_lclG2)的部分被做成函数
                        // _lclD4.subscribe("/user/queue/bidcaptcha", function (_argG1) {
                        //     _lclD11(_argG1);
                        // });
                        // _lclD11 = function (_argF1) {
                        //     _argD1(ex_updateBidCaptcha(_lclG2));
                        // }
                        let func = getInitValue(callee);
                        if (func && types.isFunctionExpression(func.node) && func.node.params.length == 1) {
                            paramName = func.node.params[0].name; paramBinding = getReference(func.get("params")[0]);
                            func.get("body").traverse({
                                CallExpression(_path) {
                                    let _callee = getIdentifierPath(_path.get("callee")), _args = _path.get("arguments");
                                    if (!_callee || _args.length !== 1) return;
                                    if (!isArgFromParam(_args[0])) return;
                                    obj = getInitValue(_args[0]);
                                }
                            });
                        }
                    }
                    if (obj) travalObjectPropries(obj, function (keyName, valuePath) {
                        if (!types.isMemberExpression(valuePath.node.object) ||
                            !isObjectAnyProperty(valuePath.get("object"), paramName, "headers")) return;
                        let { property } = valuePath.node, headerField;
                        if (types.isStringLiteral(property))
                            headerField = property.value;
                        else if (types.isIdentifier(property))
                            headerField = property.name;
                        if (headerField) catpchaHeader[keyName] = headerField;
                    });
                    param.DEST_BidCaptcha.dest.setCompare(createDestURL(url, true));
                    //在校验码对话框中用到的显示字段只有prompt和amount,而amount使用praseInt，因此不在catpchaHeader内
                    let props = displayFields["chkcodeDlg"]; if (props) {
                        for (let prop of props) if (prop in catpchaHeader)
                            param.DEST_BidCaptcha.prompt.setCompare(catpchaHeader[prop]);
                    }
                    urlPrased = true; return;
                }
                //在函数体内查找 jsonObject.xxx
                if (arrayIndex === undefined && jsonObj) for (let j of jsonObj) {
                    let binding = getReference(path, path.scope, j);
                    for (let ref of binding) if (ref.parentKey === "object") {
                        let parent = ref.parentPath; if (!parent) continue;
                        let field = getObjectProperty(parent); if (field) addObjKeys(field, field);
                    }
                }
                if (getBodyField("myBid.bidCount") && getBodyField("myBid.bidAmount") && getBodyField("myBid.dealTime")) {
                    param.DEST_MyBid.field.setCompare(getParamFields());
                    param.DEST_MyBid.dest.setCompare(createDestURL(url, true));
                    urlPrased = true; return;
                }
                if (includeError) { //包含错误文字的是DEST_error
                    param.DEST_Error.field.setCompare(getParamFields());
                    param.DEST_Error.dest.setCompare(createDestURL(url, true));
                    urlPrased = true; return;
                }
                //调用时整体赋值，则需要找函数定义
                let funcDefine, funcName;
                //callee: ex_updateCardInfo(...)
                for (let v = getInitValue(callee); !!v;) {
                    if (types.isFunctionExpression(v.node)) { funcDefine = v; break; }
                    if (types.isMemberExpression(v.node)) {
                        if (!funcName) {
                            if (!types.isIdentifier(v.node.property)) return;
                            funcName = v.node.property.name;
                        }
                        v = getInitValue(v.get("object")); continue;
                    }
                    if (funcName) v.traverse({    //funcName: function(){}
                        ObjectExpression(_path) {
                            travalObjectPropries(_path, function (keyName, valuePath) {
                                if (keyName === funcName && types.isFunctionExpression(valuePath.node))
                                    funcDefine = valuePath;
                            });
                        }
                    });
                    break;
                }
                if (!funcDefine) return;
                /**updateCardInfo: function (state, action) {
                 *  state.cardInfo = action.payload;
                 * }
                 */
                //查找 state.[xxxx] = action.payload
                let { params } = funcDefine.node, stateObj;
                if (params.length !== 2 || !types.isIdentifier(params[0]) || !types.isIdentifier(params[1])) return;
                funcDefine.traverse({
                    AssignmentExpression(_path) {
                        let { operator } = _path.node;
                        if (operator !== "=" || !isObjectProperty(_path.get("right"), params[1].name, "payload")) return;
                        if (getObjectProperty(_path.get("left"), params[0].name))
                            stateObj = _path.get("left").get("property");
                    }
                });
                if (!stateObj) return;

                let refPaths = [];
                let binding = getReference(stateObj, path.scope), isCardinfo, isResult;
                if (binding.length > 0)
                    refPaths = binding;
                else {
                    //如果当前范围内没找到，则根据selectBiddingResult的引用查找
                    binding = getReference(path, path.scope, "select" + upperFirst(stateObj.node.name));
                    for (let ref of binding) {
                        let b = getReference(stateObj, ref.scope);
                        if (b) refPaths.push.apply(refPaths, b);
                    }
                }
                for (let ref of refPaths) {
                    if (ref.parentKey === "object") {
                        let parent = ref.parentPath; if (!parent) continue;   //cardInfo.validdate
                        let field = getObjectAnyProperty(parent); if (!field) continue;
                        addObjKeys(field, field);
                        /**判断是否是CardInfo
                         * cardInfo.validdate.split("-")
                         */
                        parent = parent.parentPath; if (!parent) continue;   //cardInfo.validdate.split
                        if (parent.parentKey === "callee" && isFunction(parent.parentPath, "split")) {
                            parent = parent.parentPath;   //cardInfo.validdate.split("-")
                            if (parent.node.arguments.length === 1 &&
                                types.isStringLiteral(parent.node.arguments[0], { value: "-" }))
                                isCardinfo = true;
                        }
                    } else if (ref.parentKey === "left") {
                        /**判断是否是Dest_Message
                         * biddingResult && v.a.createElement(Qe, null)
                         */
                        let parent = ref.parentPath; if (!parent || !types.isLogicalExpression(parent.node)) continue;
                        if (parent.node.operator !== "&&" || !isCreateElement(parent.get("right"))) continue;
                        isResult = true;
                    }
                }
                if (isCardinfo) {
                    param.DEST_CardInfo.field.setCompare(getParamFields());
                    param.DEST_CardInfo.dest.setCompare(createDestURL(url, true));
                    urlPrased = true; return;
                }
                if (isResult) {
                    objKeys = {}; if (fieldResult) for (let fd of fieldResult) addObjKeys(fd, fd);
                    param.DEST_Message.field.setCompare(getParamFields());
                    param.DEST_Message.dest.setCompare(createDestURL(url, true));
                    urlPrased = true; return;
                }
                /**在updatexxx函数体内找 state.lastBid.xxx(或action.payload.xxx) === "xxx"(或enum字符串常量)
                 * updateBid里有判断 "enqueue" === q.payload.type "deal" === q.payload.type
                 */
                let constVar = new Set(), enqueue;
                funcDefine.traverse({
                    BinaryExpression(_path) {
                        function getStringOrEnum(nodePath) {    //返回字符常量或枚举值字符常量
                            let v = getStringValue(nodePath); if (v) return v;
                            if (!isObjectAnyProperty(nodePath)) return;
                            if (!(nodePath.node.object.name in enums)) return;
                            let enumAry = enums[nodePath.node.object.name];
                            if (!(nodePath.node.property.name in enumAry)) return;
                            v = enumAry[nodePath.node.property.name];
                            if (typeof v === "string") return v;
                        };
                        function getPayloadField(nodePath) {    //返回state.cardInfo.xxx 或 action.payload.xxx
                            if (!types.isMemberExpression(nodePath.node) || !types.isIdentifier(nodePath.node.property)) return;
                            if (isObjectAnyProperty(nodePath.get("object"), params[0].name, stateObj.node.name) ||
                                isObjectAnyProperty(nodePath.get("object"), params[1].name, "payload"))
                                return nodePath.node.property.name;
                        }
                        let { operator } = _path.node;
                        if (operator != "==" && operator != "===" && operator != "!=" && operator != "!==") return;
                        let stringValue = getStringOrEnum(_path.get("left")), field;
                        if (stringValue)
                            field = getPayloadField(_path.get("right"));
                        else {
                            field = getPayloadField(_path.get("left"));
                            stringValue = getStringOrEnum(_path.get("right"))
                        }
                        if (!field || !stringValue) return;
                        /**enqueue条件体里有函数调用
                        * if ("enqueue" === q.payload.type) Utils.RecvPriceRespTrackerLog(...)
                        */
                        for (let p = _path; p; p = p.parentPath) {
                            if (p.parentKey === "test" && types.isIfStatement(p.parentPath.node)) {
                                p.parentPath.get("consequent").traverse({
                                    CallExpression(__path) {
                                        if (isObjectAnyProperty(__path.get("callee"))) enqueue = stringValue;
                                    }
                                });
                                break;
                            }
                        }
                        constVar.add(stringValue);
                    }
                });
                if (constVar.size > 0) {  //如果有比较常量为Dest_BidUpdate
                    param.DEST_BidUpdate.field.setCompare(getParamFields());
                    param.DEST_BidUpdate.dest.setCompare(createDestURL(url, true));
                    if (constVar.size === 2 && enqueue && constVar.has(enqueue)) {
                        param.DEST_BidUpdate.typeEnqueue.setCompare(enqueue);
                        let ary = Array.from(constVar);
                        param.DEST_BidUpdate.typeDeal.setCompare(enqueue === ary[0] ? ary[1] : ary[0]);
                    } else
                        doOutput("***Dest_BidUpdate.type取值不符:" + constVar.toString());
                    urlPrased = true; return;
                }
            }
        });
        if (!urlPrased) doOutput("***未知订阅:" + generator(url.node).code);
    };
    function publish(url, propName) {
        let urlPrased;
        if (url && propName) {
            let fields = new Set(), valueIsMemberExpression;
            function getFields(path) {
                if (!path || !path.node) return;
                if (types.isCallExpression(path.node)) {
                    let added = false;
                    if (path.node.arguments.length > 0) travalObjectPropries(path.get("arguments")[0], function (keyName, valuePath) {
                        if (types.isMemberExpression(valuePath.node)) valueIsMemberExpression = true;
                        fields.add(keyName); added = true;
                    });
                    if (added) return;
                }
                if (path.parentKey === "callee")
                    getFields(path.parentPath);
                else if (path.parentKey === "init" &&   //var f=n.v  -> f
                    types.isVariableDeclarator(path.parentPath.node) && types.isIdentifier(path.parentPath.node.id))
                    getFields(path.parentPath.get("id"));
                else if (path.parentKey === "right" && path.parentPath.node.operator === "=" && //f=n.v -> f
                    types.isAssignmentExpression(path.parentPath.node) && types.isIdentifier(path.parentPath.node.id))
                    getFields(path.parentPath.get("left"));
                else if (types.isIdentifier(path.node)) {
                    let binding = getReference(path);
                    for (let ref of binding) if (ref !== path) getFields(ref);
                }
            };
            traverse(ast, {
                MemberExpression(path) {
                    if (types.isIdentifier(path.node.property, { name: propName })) getFields(path);
                }
            });
            if (fields.size <= 2 && valueIsMemberExpression) {
                //两个参数的是DEST_Captcha(auctionid,amount) 且 body字段赋值有MemberExpression
                // _lclB9(Object(impobj_5.exp_bidcaptcha)({
                //     auctionId: _argB1.auctionId,
                //     amount: _argD1
                // }));
                param.DEST_Captcha.dest.setCompare(createDestURL(url, false));
                param.DEST_Captcha.field.setCompare(Array.from(fields));
                urlPrased = true; return;
            } else if (fields.size >= 3) {  //三个参数的是DEST_Bid(auctionid,amount,captchaAnswer)
                param.DEST_Bid.dest.setCompare(createDestURL(url, false));
                param.DEST_Bid.field.setCompare(Array.from(fields));
                urlPrased = true; return;
            }
        }
        if (!urlPrased) doOutput("***未知Publish:" + (url ? generator(url.node).code : "null"));
    };
    traverse(ast, {
        CallExpression(path) {
            if (isFunction(path, "subscribe")) {
                let args = path.get("arguments"); if (args.length < 2) return;
                subscibe(args[0]/*url*/, args[1]/*function(){}*/);
            } else if (isFunction(path, "publish")) {
                /**bidcaptcha: function (f, q) {
                 * qq.publish({
                 * destination: "/app/bidcaptcha",
                 * ...
                 * });
                 * }
                 */
                let url, func = path.getFunctionParent();
                travalObjectPropries(path.get("arguments")[0], function (keyName, valuePath) {
                    if (keyName === "destination") url = valuePath;
                });
                if (func.parentKey === "value") func = func.parentPath.get("key");
                publish(url, func.node.name);     //url:/app/bidcaptcha; func:bidcaptcha
            } else if (isFunction(path, "ex_updateWssEndpoints") || isFunction(path, "updateWssEndpoints") || isFunction(path, "exp_updateWssEndpoints")) {
                /**
                 * if (_lclE3.endpoints) dispatch(ex_updateWssEndpoints(_lclE3.endpoints))
                 */
                let args = path.get("arguments"); if (args.length !== 1) return;
                if (types.isMemberExpression(args[0])) {
                    let id = getIdentifierPath(args[0]); if (id && id.node)
                        param.login.tradeserverPre.setCompare("\"" + id.node.name + "\":");
                }
            }
        }
    });
};

let sourcePath;
for (let i = 0; i < args.length; i++) {
    if (args[i].toLowerCase() === "/step")
        requireSteps = true;
    else if (!sourcePath) {
        sourcePath = args[i];
    } else if (!outputName) {
        outputName = args[i];
    }
}
if (!sourcePath) {
    console.log("usage: node analyzer.js resultPath [main_cvt.txt] [/step]"); return;
}
if (sourcePath.charAt(sourcePath.length - 1) !== '\\') sourcePath += "\\";
let fname = sourcePath + "verify.js";
if (!fs.existsSync(fname)) {
    console.log(fname + " not found"); return;
}
let dataVerify = fs.readFileSync(fname, 'utf8');

fname = sourcePath + "main_cvt.js";
if (!fs.existsSync(fname)) {
    console.log(fname + " not found"); return;
}
let dataMain = fs.readFileSync(fname, 'utf8');

if (!outputName)
    console.log("begin analyze...");
else
    fs.writeFileSync(outputName, "begin analyze...\r\n");
procVerify(dataVerify);
procAnalyze(dataMain);
doOutput("begin compare...");
param.compare();
if (!outputName)
    console.log("Analyze successful");
else
    console.log("Analyze successful, output file: " + outputName);
